package notify

import (
	"encoding/json"
	"fmt"
	"github.com/gomodule/redigo/redis"
	"log"
	"time"
)
type NotifyCache struct {
	r *redis.Pool
	groupExpire int32
}
func NewNotifyCache(c *Redis,expireSec int32) *NotifyCache{
	return &NotifyCache{r: NewRedis(c),groupExpire: expireSec}
}
func NewRedis(c *Redis) *redis.Pool {
	return &redis.Pool{
		MaxIdle: c.Idle,
		MaxActive: c.Active,
		IdleTimeout: time.Duration(c.IdleTimeout),
		Dial: func() (redis.Conn, error) {
			conn, err := redis.Dial(c.Network, c.Addr,
				redis.DialConnectTimeout(time.Duration(c.DialTimeout)),
				redis.DialReadTimeout(time.Duration(c.ReadTimeout)),
				redis.DialWriteTimeout(time.Duration(c.WriteTimeout)),
				redis.DialPassword(c.Auth),
			)
			if err != nil {
				return nil, err
			}
			return conn,nil
		},
	}
}
func (c *NotifyCache) SetKeyValue(key,val string) (error) {
	var err error
	conn := c.r.Get()
	defer conn.Close()
	if err = conn.Send("SET",key,val); err != nil {
		return fmt.Errorf("conn.Send(SET %s) error(%v)", key, err)
	}
	if err = conn.Send("EXPIRE", key, c.groupExpire); err != nil {
		return fmt.Errorf("conn.Send(EXPIRE %s) error(%v)", key, err)
	}
	err = conn.Flush()
	if err != nil {
		return err
	}
	result,err := redis.String(conn.Receive())
	if err != nil {
		return fmt.Errorf("conn.Receive() error(%v)", err)
	}
	log.Printf("set key(%s),val(%s) success result:%v \n",key,val,result)
	return  nil
}
func (c *NotifyCache) GetKeyValue(key string) (string,error) {
	conn := c.r.Get()
	defer conn.Close()
	conn.Send("GET",key)
	err := conn.Flush()
	if err != nil {
		return "",err
	}
	result,err := redis.String(conn.Receive())
	fmt.Printf("result:%v \n",result)
	if err == redis.ErrNil {
		return "",nil
	}
	if err != nil {
		return "",fmt.Errorf("conn.Receive() error(%v)", err)
	}
	return result,nil
}
func (c *NotifyCache) HasSended(key string,offset int32) (bool,error)  {
	conn := c.r.Get()
	defer conn.Close()
	conn.Send("GETBIT",key,offset)
	err := conn.Flush()
	if err != nil {
		return true,err
	}
	val,err := redis.Int(conn.Receive())
	if val == 0 {
		return false,nil
	}
	return true,nil
}
func (c *NotifyCache) SetSended(key string,offset int32) (bool,error){
	conn := c.r.Get()
	defer conn.Close()
	var err error
	if err = conn.Send("SETBIT",key,offset,1);err != nil {
		return false,fmt.Errorf("conn.Send(SETBIT %s) error(%v)", key, err)
	}
	if err = conn.Send("EXPIRE", key, c.groupExpire); err != nil {
		return false,fmt.Errorf("conn.Send(EXPIRE %s) error(%v)", key, err)
	}
	err = conn.Flush()
	if err != nil {
		return false,err
	}
	result,err := redis.Int(conn.Receive())
	if err != nil {
		return false,fmt.Errorf("conn.Receive() error(%v)", err)
	}
	if result == 0 {
		return false,nil
	}
	return true,nil
}

func (c *NotifyCache) SyncGroupState(groupId int64,info GroupInfo) error{
	msg,err := json.Marshal(info)
	log.Printf("syncGroupState %s",string(msg))
	if err != nil {
		return err
	}
	name :=  fmt.Sprintf("group:sync:%d",groupId)
	conn := c.r.Get()
	defer conn.Close()
	if err = conn.Send("SET",name,string(msg)); err != nil {
		return fmt.Errorf("conn.Send(SET %s) error(%v)", name, err)
	}
	if err = conn.Send("EXPIRE", name, c.groupExpire); err != nil {
		return fmt.Errorf("conn.Send(EXPIRE %s) error(%v)", name, err)

	}
	err = conn.Flush()
	if err != nil {
		return err
	}
	result,err := redis.String(conn.Receive())
	if err != nil {
		return fmt.Errorf("conn.Receive() error(%v)", err)
	}
	log.Printf("sync success result:%v \n",result)

	return nil
}
func (c *NotifyCache) GetGroupState(groupId int64) (GroupInfo,error) {
	ginfo := GroupInfo{
		Reason: "",
		Data:   nil,
	}
	name :=  fmt.Sprintf("group:sync:%d",groupId)
	conn := c.r.Get()
	defer conn.Close()
	conn.Send("GET",name)
	err := conn.Flush()
	if err != nil {
		return ginfo,err
	}
	result,err := redis.String(conn.Receive())
	fmt.Printf("result:%v \n",result)
	if err == redis.ErrNil {
		return ginfo,nil
	}
	if err != nil {
		return ginfo,fmt.Errorf("conn.Receive() error(%v)", err)
	}
	ginfo.Data = map[string]StaticInfo{}
	if result == "" {
		return ginfo,nil
	}
	err = json.Unmarshal([]byte(result),&ginfo)
	if err != nil {
		return ginfo,err
	}
	return ginfo,nil
}
type GroupInfo struct {
	State int `json:"state"`
	Reason string `json:"reason"`
	Data map[string] StaticInfo `json:"data"`
}
type StaticInfo struct {
	Success  int32 `json:"success"`
	Fail  int32 `json:"fail"`
}